/**
 * Slider control.
 * 
 * @author Andrey Anisimov <hercules@inbox.ru>
 * @see core.ui.ProgressBar, core.ui.ScrollBar
 */

import core.ui.DragWindow;
import core.util.Listeners;
import core.ui.FocusWindow;
import core.ui.Handle;

class Slider extends FocusWindow {
	
	/**
	 * Creates new slider control.
	 * 
	 * @param parent (Window) [optional] parent window
	 * @param x, y (int) [optional] coordinates
	 * @param width, height (int) [optional] control size.
	 */
	this.Slider = function(parent, x, y, width, height) {
		super(parent, x, y, width, height);
		this.vertical = false;
		this.pos = 0;
		this.min = 0;
		this.max = 100;
		this.listeners = new Listeners(['onPositionChanged']);
		this.indicator = new DragWindow(this);
		this.indicator.setStyleName('Indicator');
		this.indicator.setDirection(Handle.DIRECTION_EAST | Handle.DIRECTION_WEST);
		this.indicator.addDragListener(this);
		this.setVertical(false);
	}

	this.addChangeListener = function(listener) {
		this.listeners.add(listener);
	}

	this.removeChangeListener = function(listener) {
		this.listeners.remove(listener);
	}

	/**
	 * Sets new limits range.
	 * 
	 * @param min, max (numeric) range limits
	 */
	this.setRange = function(min, max) {
		this.min = min;
		this.max = max;
	}

	/**
	 * Returns min position value.
	 *
	 * @return numeric
	 */
	this.getRangeMin = function() {
		return this.min;
	}
	
	/**
	 * Returns max position value.
	 *
	 * @return numeric
	 */
	this.getRangeMax = function() {
		return this.max;
	}
	
	/**
	 * Sets new indicator (knob) position.
	 *
	 * @param pos (numeric) new position
	 */
	this.setPos = function(pos) {
		// Check bounds
		if (pos < this.min) {
			this.setPos(this.min);
		} else if (pos > this.max) {
			this.setPos(this.max);
		} else {
			this.pos = pos;
		}
		// Move indicator
		if (this.isVertical()) {
			var top = this.getClientHeight() - this.indicator.getHeight() - (this.pos - this.min) / (this.max - this.min) * (this.getClientHeight() - this.indicator.getHeight());
			this.indicator.setStyle('left', 'auto');
			this.indicator.setStyle('top', top + 'px');
		} else {
			var left = (this.pos - this.min) / (this.max - this.min) * (this.getClientWidth() - this.indicator.getWidth());
			this.indicator.setStyle('left', left + 'px');
			this.indicator.setStyle('top', 'auto');
		}
	}
	
	/**
	 * Returns current indicator (knob) position.
	 *
	 * @return numeric
	 */
	this.getPos = function() {
		return this.pos;
	}
	
	/**
	 * Sets whether this slider is vertical.
	 *
	 * @param vertical (bool) true if vertical, false if horisontal
	 */
	this.setVertical = function(isVertical) {
		this.removeStyleName(this.getStyleName() + (this.vertical ? '-vertical' : '-horisontal'));
		this.vertical = isVertical;
		this.addStyleName(this.getStyleName() + (this.vertical ? '-vertical' : '-horisontal'));
		this.indicator.setDirection(this.isVertical()
			? Handle.DIRECTION_NORTH | Handle.DIRECTION_SOUTH
			: Handle.DIRECTION_EAST | Handle.DIRECTION_WEST
		);
		this.setPos(this.pos);
	}
	
	/**
	 * Gets whether this slider is vertical.
	 */
	this.isVertical = function() {
		return this.vertical;
	}
	
	/**
	 * Returns progress indicator control.
	 *
	 * @return DragWindow
	 */
	this.getProgressIndicator = function() {
		return this.indicator;
	}
	
	/**
	 * @protected
	 * Gets the slider position by the screen coordinates.
	 *
	 * @param x, y (int) coordinates relatievly slider left and top.
	 */
	this.getPositionByCoords = function(x, y) {
		var pos = (this.isVertical())
			? this.max - Math.floor(y / (this.getClientHeight() - this.indicator.getHeight()) * (this.max - this.min))
			: this.min + Math.floor(x / (this.getClientWidth() - this.indicator.getWidth()) * (this.max - this.min));
		
		if (pos < this.min) {
			return this.min;
		} else if (pos > this.max) {
			return this.max;
		} else {
			return pos;
		}
	}

	/**
	 * @protected
	 */
	this.firePositionChanged = function() {
		this.listeners.call('onPositionChanged', [this]);
	}

	this.onDrag = function() {
		var pos = this.getPositionByCoords(this.indicator.getX(), this.indicator.getY());
		// If the indicator is set against the begin or the end of slider window,
		// ensure it's not out of bounds.
		if ((this.min == pos) || (this.max == pos)) {
			this.setPos(pos);
		} else {
			this.pos = pos;
		}
		this.firePositionChanged();
	}
	
	this.onmousedown = function(event, target) {
		if (this == target) {
			var pos = this.getPositionByCoords(event.getX() - this.getAbsX() - this.indicator.getWidth()/2,
				event.getY() - this.getAbsY() - this.indicator.getHeight()/2);
				
			this.setPos(pos);
			this.firePositionChanged();
		}
	}
	
	this.onkeypress = function(event) {
		// Increase or decrease by one percent.
		var p = (this.max - this.min) / 100;
		var handled = false;
		
		if (this.isVertical()) {
			if (Event.KEY_UP_ARROW == event.keyCode) {
				this.setPos(this.getPos() + p);
				handled = true;
			} else if (Event.KEY_DOWN_ARROW == event.keyCode) {
				this.setPos(this.getPos() - p);
				handled = true;
			}
		} else {
			if (Event.KEY_LEFT_ARROW == event.keyCode) {
				this.setPos(this.getPos() - p);
				handled = true;
			} else if (Event.KEY_RIGHT_ARROW == event.keyCode) {
				this.setPos(this.getPos() + p);
				handled = true;
			}
		}
		
		this.firePositionChanged();
		
		if (handled) {
			event.cancel();
		}
	}
}